/* $Id: ModificationDateType.java,v 1.2 2003/08/02 19:20:02 survey Exp $
 * 
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.medbeans.modules.search.types;


import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.util.HelpCtx;
//import org.openide.util.NbBundle;


/**
 * Test DataObject primaryFile for modificaion date.
 * <p>There are mutually exclusive criteria: between and days.
 * Between represents absolute date interval. Days represents
 * relative interval related to today.
 *
 * <p>Internally uses null as wildcard. It is presented as an empty string.
 * One of between or days must be null.
 *
 * @author  Petr Kuzel
 */
public class ModificationDateType extends DataObjectType {

    /** Serial version UID. */
    static final long serialVersionUID = 4L;

    /** Holds value of property matchBefore. */
    private Date matchBefore;

    /** Holds value of property matchAfter. */
    private Date matchAfter;

    /** Holds value of property day. */
    private Short days;

    
    /**
     * Does the DataObject pass this criterion?
     * @param dataobject to be examined
     * @return true if pass */
    public boolean testDataObject(DataObject dobj) {

        FileObject fo = dobj.getPrimaryFile();

        // it is strange
        if(fo == null)
            return false;

        // Primary File Modification Date
        Date date = fo.lastModified();

        boolean hit = testDays(date) && testAfter(date) && testBefore(date);

        if(hit)
            return true;
        else
            return false;
    }

    /** Is within range? */
    private boolean testAfter(Date date) {
        if (matchAfter == null) return true;
        return date.compareTo(matchAfter)>=0;
    }

    /** Is within range? */
    private boolean testBefore(Date date) {
        if (matchBefore == null) return true;
        return date.compareTo(matchBefore)<=0;
    }

    /** Is within 24 hours range? */
    private boolean testDays(Date date) {
        if (days == null) return true;
        return (System.currentTimeMillis() - date.getTime()) < days.shortValue()*1000L*60L*60L*24L;
    }

    // -------------- before -------------------
    /** Getter for property matchBefore.
     *@return Value of property matchBefore. */
    public Date getMatchBeforeAsDate() {
        return new FormattedDate(matchBefore);
    }

    /** Getter of <code>matchBefore</code> property. */
    public String getMatchBefore() {
        if (matchBefore == null)
            return ""; // NOI18N
        else
            return getMatchBeforeAsDate().toString();
    }

    /** Setter for property <code>matchBefore</code>.
     * @exception IllegalArgumentException if null passed */
    public void setMatchBefore(String before) {
        try {
            setMatchBeforeImpl(before);
            setValid(true);
        } catch (IllegalArgumentException ex) {
            setValid(false);
            throw ex;
        }
    }

    /** Helper method for setting <code>matchBefore</code> property. */
    private void setMatchBeforeImpl(String matchBefore) {
        if(matchBefore == null) throw new IllegalArgumentException();

        if(matchBefore.equals("")) { // NOI18N
            setMatchBeforeByDate(null);
            return;
        }

        try {
            setMatchBeforeByDate(new FormattedDate(matchBefore));

        } catch (ParseException ex) {
            throw new IllegalArgumentException();
        }

    }

    /** Sets property <code>matchBefore</code> according specified date.
     * @param matchBefore new value of property matchBefore */
    public void setMatchBeforeByDate(Date matchBefore) {
        //let date represent one milisecond before midnight
        if (matchBefore != null) {

            GregorianCalendar cal = new GregorianCalendar();
            cal.setTime(matchBefore);

            cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY));
            cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE));
            cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND));
            cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND));

            matchBefore = cal.getTime();
        }

        //do it

        Date old = this.matchBefore;
        this.matchBefore = matchBefore;
        days = null;
    }


    // --------------- after ---------------
    /** Gets for property <code>matchAfter</code> according specified date.
     * @return value of property matchAfter */
    public Date getMatchAfterAsDate() {
        return new FormattedDate(matchAfter);
    }

    /** Getter for property <code>matchAfter</code>. */
    public String getMatchAfter() {

        if (matchAfter == null)
            return ""; // NOI18N
        else
            return getMatchAfterAsDate().toString();
    }

    /** Setter for property <code>matchAfter</code>.
     * @exception IllegalArgumentException if null passed */
    public void setMatchAfter(String after) {
        try {
            setMatchAfterImpl(after);
            setValid(true);
        } catch (IllegalArgumentException ex) {
            setValid(false);
            throw ex;
        }
    }

    /** Helper method which sets <code>matchAfter</code> property. */
    private void setMatchAfterImpl(String matchAfter) {

        if (matchAfter == null) throw new IllegalArgumentException();

        if (matchAfter.equals("")) { // NOI18N
            setMatchAfterByDate(null);
            return;
        }

        try {
            setMatchAfterByDate(new FormattedDate(matchAfter));

        } catch (ParseException ex) {
            throw new IllegalArgumentException();
        }

    }

    /** Setter for property matchAfter.
     *@param matchAfter New value of property matchAfter.
     */
    public void setMatchAfterByDate(Date matchAfter) {

        //let date represent midnight

        if (matchAfter != null) {

            GregorianCalendar cal = new GregorianCalendar();
            cal.setTime(matchAfter);

            cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
            cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
            cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
            cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND));

            matchAfter = cal.getTime();
        }

        //do it

        Date old = this.matchAfter;
        this.matchAfter = matchAfter;
        days = null;
    }

    //  ------------ days handling ----------------------
    /** Gets property <code>days</code> ad short.
     * @return value of property day */
    public Short getDaysAsShort() {
        return days;
    }

    /** Getter for property <code>days</code>. */
    public String getDays() {
        if(days == null) {
            return ""; // NOI18N
        } else {
            return days.toString();
        }
    }

    /** Setter for property <code>days</code>.
     * @param String to be parsed as short */
    public void setDays(String days) {
        try {
            setDaysImpl(days);
            setValid(true);
        } catch (IllegalArgumentException ex) {
            setValid(false);
            throw ex;
        }
    }

    /** Sets <code>days</code> property. Helper method. */
    private void setDaysImpl(String days) {

        if("".equals(days)) { // NOI18N
            setDaysByShort(null);
            return;
        }

        try {
            DecimalFormat format = new DecimalFormat();
            setDaysByShort(new Short(format.parse(days).shortValue()));

        } catch (ParseException ex) {
            throw new IllegalArgumentException();
        }

    }

    /** Setter for property day.
     * @param day new value of property day */
    private void setDaysByShort(Short days) {

        Short old = this.days;
        this.days = days;

        matchAfter = null;
        matchBefore = null;
    }

    /** Gets help context for this search type.
     * Implements superclass abstract method. */
    public HelpCtx getHelpCtx() {
        return new HelpCtx(ModificationDateType.class);
    }
    
    
    /**
     * Date using default locale formatting.
     * Provide overridden constructor and toString() method. */
    static class FormattedDate extends Date {

        /** Default locale formattor. */
        private static DateFormat format;
        /** isNull property */
        private transient boolean isNull = true;

        /** Init static fields */
        static {
            format = SimpleDateFormat.getDateInstance();
        }

        /** Create new object.
        * @param date null is ambiguous -> today.
        */
        public FormattedDate(Date date) {
            super(date == null ? new Date().getTime() : date.getTime() );
            isNull = date == null;
        }

        /** Create new object.
         * @param Use default locale formatting while parsing date */
        public FormattedDate(String date) throws ParseException {
            super( format.parse(date).getTime() );
            isNull = date == null;
        }

        /** Use defalt locale formatting. */
        public String toString() {
            return format.format(this);
        }

        /**
         * Extra handling of equals(null). Return true if
         * this Date represents empty string value. */
        public boolean equals(Object obj){
            if (obj == null && isNull) return true;
            else return super.equals(obj);
        }
    }
    
}
